using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using SumerCoreDevOps.Infrastructure.Tcp;
using SumerCoreDevOps.Shared.Enums;
using SumerCoreDevOps.Shared.Models.Messages;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;

namespace SumerCoreDevOps.ExecuteNodeApi.Services;

/// <summary>
/// 心跳服务
/// </summary>
public class HeartbeatService : IHostedService, IDisposable
{
    private readonly ILogger<HeartbeatService> _logger;
    private readonly TcpMultiMasterClient _tcpClient;
    private readonly NodeRegistrationService _registrationService;
    private Timer? _timer;
    private int _runningTaskCount;

    // For cross-platform CPU usage tracking
    private long _lastCpuTime;
    private DateTime _lastCpuCheck = DateTime.UtcNow;

    public HeartbeatService(
        ILogger<HeartbeatService> logger,
        TcpMultiMasterClient tcpClient,
        NodeRegistrationService registrationService)
    {
        _logger = logger;
        _tcpClient = tcpClient;
        _registrationService = registrationService;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("心跳服务已启动");

        // 每30秒发送一次心跳
        _timer = new Timer(SendHeartbeat, null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(30));

        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("心跳服务已停止");
        _timer?.Change(Timeout.Infinite, 0);
        return Task.CompletedTask;
    }

    /// <summary>
    /// 重启心跳服务
    /// </summary>
    public void Restart()
    {
        _logger.LogInformation("正在重启心跳服务...");

        // 停止现有定时器
        _timer?.Change(Timeout.Infinite, 0);
        _timer?.Dispose();

        // 重新启动定时器
        _timer = new Timer(SendHeartbeat, null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(30));

        _logger.LogInformation("心跳服务已重启");
    }

    private async void SendHeartbeat(object? state)
    {
        try
        {
            if (!_tcpClient.IsConnected())
            {
                _logger.LogWarning("未连接到任何主节点，跳过心跳");
                return;
            }

            var heartbeat = new HeartbeatMessage
            {
                NodeId = _registrationService.NodeId,
                RunningTaskCount = _runningTaskCount,
                CpuUsage = GetCpuUsage(),
                MemoryUsage = GetMemoryUsage(),
                AvailableDiskSpaceMB = GetAvailableDiskSpace()
            };

            // 向所有已连接的主节点发送心跳
            await _tcpClient.SendToAllAsync(TcpMessageType.Heartbeat, heartbeat);

            var connectedMasters = _tcpClient.GetConnectedMasters();
            _logger.LogDebug("已向 {Count} 个主节点发送心跳: CPU={CPU}%, Memory={Memory}%, Tasks={Tasks}",
                connectedMasters.Count, heartbeat.CpuUsage, heartbeat.MemoryUsage, heartbeat.RunningTaskCount);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "发送心跳失败");
        }
    }

    private double GetCpuUsage()
    {
        try
        {
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                return GetCpuUsageWindows();
            }
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
            {
                return GetCpuUsageLinux();
            }
            else
            {
                // macOS or other platforms - use process-based estimation
                return GetCpuUsageProcess();
            }
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, "获取CPU使用率失败");
            return 0.0;
        }
    }

    [SupportedOSPlatform("windows")]
    private double GetCpuUsageWindows()
    {
        try
        {
            var cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total");
            cpuCounter.NextValue(); // 第一次调用返回0
            Thread.Sleep(100);
            return Math.Round(cpuCounter.NextValue(), 2);
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, "Windows CPU计数器失败，使用备用方法");
            return GetCpuUsageProcess();
        }
    }

    private double GetCpuUsageLinux()
    {
        try
        {
            // Read /proc/stat for system-wide CPU usage
            var lines = File.ReadAllLines("/proc/stat");
            var cpuLine = lines.FirstOrDefault(l => l.StartsWith("cpu "));
            if (cpuLine == null) return 0.0;

            var values = cpuLine.Split(' ', StringSplitOptions.RemoveEmptyEntries);
            if (values.Length < 5) return 0.0;

            // CPU time = user + nice + system + idle + iowait + irq + softirq
            long totalTime = 0;
            long idleTime = 0;
            for (int i = 1; i < Math.Min(values.Length, 8); i++)
            {
                if (long.TryParse(values[i], out long val))
                {
                    totalTime += val;
                    if (i == 4) idleTime = val; // idle is 4th value
                }
            }

            // Calculate CPU usage percentage
            var now = DateTime.UtcNow;
            var timeDiff = (now - _lastCpuCheck).TotalMilliseconds;

            if (timeDiff < 100) // Too soon, return cached value
            {
                return 0.0;
            }

            var cpuTimeDiff = totalTime - _lastCpuTime;
            if (cpuTimeDiff <= 0)
            {
                _lastCpuTime = totalTime;
                _lastCpuCheck = now;
                return 0.0;
            }

            // Store for next calculation
            var cpuUsage = 100.0 * (1.0 - ((double)idleTime / totalTime));
            _lastCpuTime = totalTime;
            _lastCpuCheck = now;

            return Math.Round(Math.Max(0, Math.Min(100, cpuUsage)), 2);
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, "Linux /proc/stat读取失败，使用备用方法");
            return GetCpuUsageProcess();
        }
    }

    private double GetCpuUsageProcess()
    {
        try
        {
            // Fallback: use current process CPU time as approximation
            using var process = Process.GetCurrentProcess();
            var startTime = DateTime.UtcNow;
            var startCpuUsage = process.TotalProcessorTime;

            Thread.Sleep(100);

            var endTime = DateTime.UtcNow;
            var endCpuUsage = process.TotalProcessorTime;

            var cpuUsedMs = (endCpuUsage - startCpuUsage).TotalMilliseconds;
            var totalMsPassed = (endTime - startTime).TotalMilliseconds;
            var cpuUsageTotal = cpuUsedMs / (Environment.ProcessorCount * totalMsPassed);

            return Math.Round(cpuUsageTotal * 100, 2);
        }
        catch
        {
            return 0.0;
        }
    }

    private double GetMemoryUsage()
    {
        var gcInfo = GC.GetGCMemoryInfo();
        var totalMemory = gcInfo.TotalAvailableMemoryBytes;
        var usedMemory = GC.GetTotalMemory(false);
        return Math.Round((double)usedMemory / totalMemory * 100, 2);
    }

    private long GetAvailableDiskSpace()
    {
        try
        {
            var drive = DriveInfo.GetDrives().FirstOrDefault(d => d.IsReady);
            return drive?.AvailableFreeSpace / 1024 / 1024 ?? 0;
        }
        catch
        {
            return 0;
        }
    }

    public void IncrementTaskCount()
    {
        Interlocked.Increment(ref _runningTaskCount);
    }

    public void DecrementTaskCount()
    {
        Interlocked.Decrement(ref _runningTaskCount);
    }

    public void Dispose()
    {
        _timer?.Dispose();
    }
}
